iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
AI Engineering

30 天 AI eval 實戰:一隻 PM agent,改完之後怎麼確定它還是對的系列 第 25 篇

Day 25|改一行 prompt 之後,用一個命令重跑整份 dataset

  • 分享至 

  • xImage
  •  

改了一行 prompt,要知道結果有沒有變差,就用一個命令把整份 dataset 重跑一遍,每趟在乾淨環境裡跑、逐個欄位給分、每筆重跑同樣的次數;報表也要記下實際跑了幾趟,才分得出沒通過和沒跑完。

需求釐清 agent 會讀一張 ticket 的描述與驗收條件(AC),整理成 Goal、Scope in、Scope out、AC 標註四個區塊,AC 標註是它替有問題的驗收條件標上的問題類別。

前面量過它標的問題每三個只有一個是真的,於是決定改 prompt,把 unverifiable(不可驗證)與 mismatch(與描述不符)兩行定義寫窄,條件是 recall 不能掉破七成,recall 是人工標好的問題有幾成被 agent 找到。

改完之後只挑優惠碼那張卡看一眼,看不出另外 15 筆的 recall 有沒有掉。要回答這個問題,就得把 16 筆 case 在同樣的條件下各重跑幾次、逐筆給分再匯總。這些步驟前面已經分別做好,也已經由 npm run run-all 接成一個命令,這篇把這個命令拆開,看每一步排除了什麼,以及它的總表還少記了什麼。

一個命令裡的五個步驟

假的 ticket store(本機)代替真正的看板讓 agent 讀寫卡片,跑完就丟;fixture 保存錄製時查到的外部回應,有檔就重播、沒檔就錄製;goal state 是人工事先寫好的通過條件,scorer 依它逐個欄位比對。給分只用 scorer 的規則,模型裁判 judge 還沒有接進這個命令。

npm run run-all -- --k 3 會把 dataset/ 裡的 16 筆 case 各跑 3 趟,k 是同一筆 case 重跑的次數。每一步各自排除一種讓紅字不可信的情況:

步驟 程式 排除的情況
每趟現建一張卡 FakeStore.fromTicket 上一趟寫回的內容留到這一趟
重播外部回應 openFixture、ReplayingSource 外部資料這次和上次查到的不同
依 goal state 給分 scoreCase 人眼逐張比對時看漏
同一筆跑 k 趟 run-all 的迴圈 只跑一次,通過可能是碰巧
匯總成一張總表 run-all 最後寫出的總表 只挑幾張卡看

重播時若查了 fixture 沒錄過的內容,ReplayingSource 會拋出 MissingFixtureError,錯誤裡帶著工具名 tool 與參數 args,這一趟當場中斷。

前三步在 runOnce 裡,第四步是 src/cli/run-all.ts 在它外面加的一層迴圈,第 50 到 79 行是這樣:

const rows: CaseRow[] = [];
for (const { path, case: testCase } of ready) {
  const annotation = expectedFlags(testCase.goalState);
  const counts = [];
  let passes = 0;
  let flagged: string[] = [];

  for (let i = 1; i <= k; i++) {
    try {
      const run = await runOnce({ casePath: path, outDir: join(batchDir, testCase.id, `run-${i}`) });
      flagged = run.actual.ac_flags ?? [];
      counts.push(compareFlags(annotation, flagged));
      if (run.score.pass) passes += 1;
    } catch (err) {
      if (!(err instanceof MissingFixtureError)) throw err;
      console.error(`  ${testCase.id} run ${i} 停在 fixture 對不上:${err.tool}`);
    }
  }

  rows.push({
    caseId: testCase.id,
    category: testCase.category,
    k,
    passes,
    counts: sumCounts(counts),
    annotation,
    flagged,
  });
  console.log(`  ${testCase.id}  ${passes}/${k} 過`);
}

迴圈跑完後,同檔第 81 到 87 行把每筆的結果寫成總表 runs/<時間>-all.md,另存一份 all.json。完成的每一趟,最終卡片、執行紀錄與分數都寫進 runs/<批次>/<case>/run-<i>/,總表上的分數可以回頭打開那一趟的產出核對。

跑一次要多久

用 gpt-5.4-mini 跑過一次,這批執行紀錄的總表第 3 行是:

model `gpt-5.4-mini` prompt `cd590b9ba593` 16 筆 每筆 k=3

其中 cd590b9ba593 是 prompt 的版本編號,這批從第一趟開始到最後一趟結束是 3 分 9 秒,有 12 筆 case 在這批才開始錄 fixture,各有一趟是錄製,其餘 33 趟都是重播。

第 5 行是整份的 precision 34%、recall 78%,precision 是 agent 標的問題有幾成是對的。把兩行定義寫窄之後重跑同一個命令,就有兩張總表可以逐列對照,但要用來判斷 recall 有沒有掉破七成,還得先核對兩批實際納入計算的 case 與趟數。

報表還要記下完成幾趟

同一張總表寫每筆 k=3,目錄裡卻只有 45 趟。merged-concerns-001 和 noise-over-signal-002 缺 run-2,irreversible-push-003 缺 run-3,這三列在表上仍寫 0/3。0/3 的分母是表頭的 k,缺的那趟沒有產出、沒經過 scorer,只是沒加進 passes,看起來和判定失敗一樣;同一列的 precision 與 recall 卻只算完成的兩趟,表上看不出分母換了。

若改 prompt 後缺的是不同 case 或不同趟數,納入計算的樣本也會改變,recall 的升降就不能直接歸因於 prompt。列出缺趟只是讓差異看得見,缺趟尚未處理前,完成部分的 recall 即使超過七成,也不足以確認整份 dataset 守住了門檻。

缺的那趟為什麼停下,沒有檔案可以查。迴圈的 catch 只接住 MissingFixtureError,在終端機印一行就接著跑下一趟,其他錯誤會讓整批中止、不寫總表;runOnce 又要等 agent 跑完才建立該趟目錄,所以停下的那趟不留任何檔案。

merged-concerns-001 和 noise-over-signal-002 的 fixture 是這批第一趟錄的,irreversible-push-003 的則在這批執行前就已錄好,三份都是 calls: [],錄製那趟沒查任何外部資料,之後只要有一趟去查就會對不上。總表寫得出來,所以最可能是這三趟查了 fixture 沒錄過的內容,但終端機輸出沒有保存,這只是從程式推得的。

harness 是把 case 逐筆跑起來的那段程式,在這裡就是 run-all。討論同一筆 case 要重跑幾次時,已經定過 harness 要記下要求的 k、實際完成的次數與參與計算的次數,對不上就列出缺哪一次,run-all 的總表並沒有照這條記。

每一列要分開寫要求、完成、通過幾趟,比值欄改叫 Passed / Completed,表示通過趟數除以完成趟數,停下的那趟另列未完成。照這個格式,兩趟失敗、一趟中斷要寫成 0/2,另列未完成 1 趟;0/3 才表示三趟都完成、都判定失敗。

catch 也要把 case、趟次和錯誤裡的 tool、args 寫進 all.json,不能只印在終端機。

總結

改一行 prompt 之後,可以用 npm run run-all -- --k 3 重跑整份 dataset,每趟現建卡片、有 fixture 就重播、依 goal state 給分,每筆跑 3 趟再匯總,整批 3 分 9 秒,改前改後的兩張總表可以逐列對照。

這張總表還要如實記下每筆要求、完成、通過幾趟,並把中斷的原因寫進檔案。前後缺趟不同時,不能只憑完成部分的 recall 判斷改動是否守住七成門檻。能重跑之後,還沒決定的是拿結果做什麼,哪些動作一律擋下,分數多少才算可以上線。


上一篇
Day 24|AI 的判定,要和人工逐筆核對
下一篇
Day 26|收不回的動作,要把 7 個工具逐個列出來
系列文
30 天 AI eval 實戰:一隻 PM agent,改完之後怎麼確定它還是對的 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言